
//package java_cup;

//import java.util.Dictionary;

/**
 * This class represents a production in the grammar. It contains a LHS non
 * terminal, and an array of RHS EmitSymbols. As various transformations are done on
 * the RHS of the production, it may shrink. As a result a separate length is
 * always maintained to indicate how much of the RHS array is still valid.
 * <p>
 * 
 * I addition to construction and manipulation operations, AllProdctions provide
 * methods for factoring out actions (see remove_embedded_actions()), for
 * computing the nullable of the production (i.e., can it derive the empty
 * string, see check_nullable()), and operations for computing its first set
 * (i.e., the set of terminals that could appear at the beginning of some string
 * derived from the production, see check_first_set()).
 * 
 * @see java_cup.production_part
 * @see java_cup.symbol_part
 * @see java_cup.action_part
 * @version last updated: 7/3/96
 * @author Frank Flannery
 */

public class ProductionDraft {

    /*-----------------------------------------------------------*/
    /*--- Constructor(s) ----------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Full constructor. This constructor accepts a LHS non terminal, an array of
     * RHS parts (including terminals, non terminals, and actions), and a string for
     * a const reduce actionPart. It does several manipulations in the process of
     * creating a production object. After some validity checking it translates
     * labels that appear in actions into code for accessing objects on the runtime
     * parse stack. It them merges adjacent actions if they appear and moves any
     * trailing actionPart into the const reduce actions string. Next it removes any
     * embedded actions by factoring them @out with new actionPart AllProdctions. Finally
     * it assigns a unique index to the production.
     * <p>
     *
     * Factoring @out of actions is accomplished by creating new "hidden" non
     * terminals. For example if the production was originally:
     * 
     * <pre>
     *    A ::= B {actionPart} C D
     * </pre>
     * 
     * then it is factored into two AllProdctions:
     * 
     * <pre>
     *    A ::= B X C D
     *    X ::= {actionPart}
     * </pre>
     * 
     * (where X is a unique new non terminal). This has the effect of placing AllVns
     * actions at the end where they can be handled as part of a reduce by the
     * CUPParser.
     */
    public ProductionDraft(Vn left, ProductionPart[] rhsParts, int rightLength, String actionStr)
        /*throws internal_error*/ {
        int i;
        ActionPart tail_action;
        String declare_str;
        int rightlen = rightLength;

        /* remember the length */
        if (rightLength >= 0)
            _rhs_length = rightLength;
        else if (rhsParts != null)
            _rhs_length = rhsParts.Length;
        else
            _rhs_length = 0;

        /* make sure we have a valid _left-hand-side */
        if (left == null)
            throw new InternalError("Attempt to construct a production with a null LHS");

        /*
         * I'm not translating labels anymore, I'm adding code to declare labels as
         * valid variables. This way, the users code string is untouched 6/96 frankf
         */

        /*
         * check if the last part of the _right hand side is an actionPart. If it is, it
         * won't be on the stack, so we don't want to count it in the rightlen. Then
         * when we search down the stack for a TheSymbolDraft, we don't try to search past
         * actionPart
         */

        if (rightLength > 0) {
            if (rhsParts[rightLength - 1].IsAction) {
                rightlen = rightLength - 1;
            }
            else {
                rightlen = rightLength;
            }
        }

        /* get the generated declaration code for the necessary labels. */
        declare_str = declareLabels(rhsParts, rightlen, actionStr);

        if (actionStr == null)
            actionStr = declare_str;
        else
            actionStr = declare_str + actionStr;

        /* count use of lhs */
        left.IncreaseUseCount();

        /* create the part for _left-hand-side */
        _lhs = new SymbolPart(left);

        /* merge adjacent actions (if any) */
        _rhs_length = mergeAdjacentActions(rhsParts, _rhs_length);

        /* strip off any trailing actionPart */
        tail_action = stripTrailingAction(rhsParts, _rhs_length);
        if (tail_action != null)
            _rhs_length--;

        /*
         * Why does this run through the _right hand side happen over and over? here a
         * quick combination of two prior runs plus one I wanted of my own frankf
         * 6/25/96
         */
        /* allocate and copy over the _right-hand-side */
        /* count use of each rhs SymbolDraft */
        _rhs = new ProductionPart[_rhs_length];
        for (i = 0; i < _rhs_length; i++) {
            _rhs[i] = rhsParts[i];
            if (!_rhs[i].IsAction) {
                ((SymbolPart)_rhs[i]).TheSymbolDraft.IncreaseUseCount();
                if (((SymbolPart)_rhs[i]).TheSymbolDraft is Vt) {
                    _rhs_prec = ((Vt)((SymbolPart)_rhs[i]).TheSymbolDraft).PrecedenceNum;
                    _rhs_assoc = ((Vt)((SymbolPart)_rhs[i]).TheSymbolDraft).PrecedenceSide;
                }
            }
        }

        /*
         * now actionPart string is really declaration string, so put it in front! 6/14/96
         * frankf
         */
        if (actionStr == null)
            actionStr = "";
        if (tail_action != null && tail_action.CodeString != null)
            actionStr = actionStr + "\t\t" + tail_action.CodeString;

        /* stash the actionPart */
        _actionPart = new ActionPart(actionStr);

        /* rewrite production to remove any embedded actions */
        removeEmbeddedActions();

        /* assign an index */
        _index = next_index++;

        /* put us in the global collection of AllProdctions */
        _index2Production.Add(_index, this);

        /* put us in the production list of the lhs non terminal */
        left.addProduction(this);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Constructor with no actionPart string. */
    public ProductionDraft(Vn lhsSym,
        ProductionPart[] rhsParts, int rhsL) /*throws internal_error*/
        : this(lhsSym, rhsParts, rhsL, null) {
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /*
     * Constructor with precedence and associativity of production contextually
     * define
     */
    public ProductionDraft(Vn lhsSym,
        ProductionPart[] rhsParts, int rhsL, String actionStr, int precNum,
        int precSide) /*throws internal_error*/
        : this(lhsSym, rhsParts, rhsL, actionStr) {
        /* set the precedence */
        setPrecedenceNum(precNum);
        setPrecedenceSide(precSide);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /*
     * Constructor w/ no actionPart string and contextual precedence defined
     */
    public ProductionDraft(Vn lhsSym,
        ProductionPart[] rhsParts, int rhsL, int precNum, int precSide)
        /*throws internal_error*/
        : this(lhsSym, rhsParts, rhsL, null) {
        /* set the precedence */
        setPrecedenceNum(precNum);
        setPrecedenceSide(precSide);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /*-----------------------------------------------------------*/
    /*--- (Access to) Static (Class) Variables ------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Table of AllVns AllProdctions. Elements are stored using their index as the key.
     */
    public static Dictionary<int, ProductionDraft> _index2Production = new();

    /** Access to AllVns AllProdctions. */
    public static IEnumerable<ProductionDraft> AllProductions() {
        return _index2Production.Values;
    }

    /** Lookup a production by index. */
    public static ProductionDraft Find(int indx) {
        return _index2Production[indx];
    }

    // Hm Added clear to clear AllVns static fields
    public static void Clear() {
        _index2Production.Clear();
        next_index = 0;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Total Count of AllProdctions. */
    public static int Count => _index2Production.Count;

    /** Static counter for assigning unique index numbers. */
    public static int next_index;

    /*-----------------------------------------------------------*/
    /*--- (Access to) Instance Variables ------------------------*/
    /*-----------------------------------------------------------*/

    /** The _left hand side non-terminal. */
    public SymbolPart _lhs;

    /** The _left hand side non-terminal. */
    public SymbolPart lhs => _lhs;

    /** The precedence of the rule */
    public int _rhs_prec = -1;
    public int _rhs_assoc = -1;

    /** Access to the precedence of the rule */
    public int precedenceNum => _rhs_prec;

    public int precedenceSide => _rhs_assoc;

    /** Setting the precedence of a rule */
    public void setPrecedenceNum(int precNum) {
        _rhs_prec = precNum;
    }

    public void setPrecedenceSide(int precSide) {
        _rhs_assoc = precSide;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** A collection of parts for the _right hand side. */
    public ProductionPart[] _rhs;

    /** Access to the collection of parts for the _right hand side. */
    public ProductionPart rhs(int indx) /*throws internal_error*/ {
        if (indx >= 0 && indx < _rhs_length)
            return _rhs[indx];
        else
            throw new InternalError("Index @out of range for _right hand side of production");
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** How much of the _right hand side array we are presently using. */
    public int _rhs_length;

    /** How much of the _right hand side array we are presently using. */
    public int rhsLength => _rhs_length;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * An _actionPart containing code for the actionPart to be performed when we reduce
     * with this production.
     */
    public ActionPart _actionPart;

    /**
     * An action_part containing code for the actionPart to be performed when we reduce
     * with this production.
     */
    public ActionPart actionPart => _actionPart;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Index Count of the production. */
    public int _index;

    /** Index Count of the production. */
    public int Index => _index;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Count of Count of reductions using this production. */
    public int _num_reductions = 0;

    /** Count of Count of reductions using this production. */
    public int numReductions => _num_reductions;

    /** Increment the count of reductions with this non-terminal */
    public void IncreaseReductionUse() {
        _num_reductions++;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Is the nullable of the production known or unknown? */
    public bool _nullable_known = false;

    /** Is the nullable of the production known or unknown? */
    public bool nullableKnown => _nullable_known;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Nullability of the production (can it derive the empty string). */
    public bool _nullable = false;

    /** Nullability of the production (can it derive the empty string). */
    public bool nullable => _nullable;

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * First set of the production. This is the set of terminals that could appear
     * at the front of some string derived from this production.
     */
    public VtSet _first_set = new VtSet();

    /**
     * First set of the production. This is the set of terminals that could appear
     * at the front of some string derived from this production.
     */
    public VtSet FirstSet => _first_set;

    /*-----------------------------------------------------------*/
    /*--- Static Methods ----------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Determine if a given character can be a label id starter.
     * 
     * @param c the character in question.
     */
    public static bool isIdStart(char c) {
        return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_';

        // later need to handle non-8-bit chars here
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Determine if a character can be in a label id.
     * 
     * @param c the character in question.
     */
    public static bool isIdChar(char c) {
        return isIdStart(c) || c >= '0' && c <= '9';
    }

    /*-----------------------------------------------------------*/
    /*--- General Methods ---------------------------------------*/
    /*-----------------------------------------------------------*/

    /**
     * Return label declaration code
     * 
     * @param labelname  the label name
     * @param stack_type the stack type of label?
     * @author frankf
     */
    public String makeDeclaration(String labelname, String stackType, int offset) {
        String ret;

        /* Put in the _left/_right value labels */
        if (Emit.lrValues) {
            if (!Emit.locations)
                ret = "\t\tint " + labelname + "_left = (" + Emit.pre("stack") +
                    // TUM 20050917
                    ((offset == 0) ? ".Peek()" : (".ElementAt(" + Emit.pre("Peek") + "-" + offset + ")")) + ")._left;\n"
                    + "\t\tint " + labelname + "_right = (" + Emit.pre("stack") +
                    ((offset == 0) ? ".Peek()" : (".ElementAt(" + Emit.pre("Peek") + "-" + offset + ")")) + ")._right;\n";
            else
                ret = "\t\tLocation " + labelname + "xleft = ((java_cup.runtime.ComplexSymbolFactory.ComplexSymbol)"
                    + Emit.pre("stack") +
                    ((offset == 0) ? ".Peek()" : (".ElementAt(" + Emit.pre("Peek") + "-" + offset + ")")) + ").xleft;\n"
                    + "\t\tLocation " + labelname + "xright = ((java_cup.runtime.ComplexSymbolFactory.ComplexSymbol)"
                    + Emit.pre("stack") +
                    ((offset == 0) ? ".Peek()" : (".ElementAt(" + Emit.pre("Peek") + "-" + offset + ")")) + ").xright;\n";
        }
        else
            ret = "";

        /* otherwise, just declare label. */
        return ret + "\t\t" + stackType + " " + labelname + " = " + Emit.pre("stack") +
            ((offset == 0) ? ".Peek()" : (".ElementAt(" + Emit.pre("Peek") + "-" + offset + ")")) + ".<" + stackType + ">value();\n";

    }
    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Declare label names as valid variables within the actionPart string
     * 
     * @param rhs          array of RHS parts.
     * @param rhs_len      how much of rhs to consider valid.
     * @param final_action the const actionPart string of the production.
     * @param lhs_type     the object type associated with the LHS SymbolDraft.
     */
    public String declareLabels(ProductionPart[] rhs, int rhsLen, String finalAction) {
        String declaration = "";

        SymbolPart part;
        int pos;

        /* walk down the parts and extract the labels */
        for (pos = 0; pos < rhsLen; pos++) {
            if (!rhs[pos].IsAction) {
                part = (SymbolPart)rhs[pos];
                String label;
                /* if it has a label, make declaration! */
                if ((label = part.Label) != null || Emit._xmlactions) {
                    if (label == null)
                        label = part.TheSymbolDraft.Name + pos;
                    declaration = declaration + makeDeclaration(label, part.TheSymbolDraft.StackType, rhsLen - pos - 1);
                }
            }
        }
        return declaration;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Helper routine to merge adjacent actions in a set of RHS parts
     * 
     * @param rhs_parts array of RHS parts.
     * @param len       amount of that array that is valid.
     * @return remaining valid length.
     */
    public int mergeAdjacentActions(ProductionPart[] rhsParts, int len) {
        int from_loc, to_loc, merge_cnt;

        /* bail @out early if we have no work to do */
        if (rhsParts == null || len == 0)
            return 0;

        merge_cnt = 0;
        to_loc = -1;
        for (from_loc = 0; from_loc < len; from_loc++) {
            /* do we go in the current position or one further */
            if (to_loc < 0 || !rhsParts[to_loc].IsAction || !rhsParts[from_loc].IsAction) {
                /* next one */
                to_loc++;

                /* clear the way for it */
                if (to_loc != from_loc)
                    rhsParts[to_loc] = null;
            }

            /* if this is not trivial? */
            if (to_loc != from_loc) {
                /* do we merge or copy? */
                if (rhsParts[to_loc] != null && rhsParts[to_loc].IsAction && rhsParts[from_loc].IsAction) {
                    /* merge */
                    rhsParts[to_loc] = new ActionPart(
                        ((ActionPart)rhsParts[to_loc]).CodeString + ((ActionPart)rhsParts[from_loc]).CodeString);
                    merge_cnt++;
                }
                else {
                    /* copy */
                    rhsParts[to_loc] = rhsParts[from_loc];
                }
            }
        }

        /* return the used length */
        return len - merge_cnt;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Helper routine to strip a trailing actionPart off rhs and return it
     * 
     * @param rhs_parts array of RHS parts.
     * @param len       how many of those are valid.
     * @return the removed actionPart part.
     */
    public ActionPart stripTrailingAction(ProductionPart[] rhsParts, int len) {
        ActionPart result;

        /* bail @out early if we have nothing to do */
        if (rhsParts == null || len == 0)
            return null;

        /* see if we have a trailing actionPart */
        if (rhsParts[len - 1].IsAction) {
            /* snip it @out and return it */
            result = (ActionPart)rhsParts[len - 1];
            rhsParts[len - 1] = null;
            return result;
        }
        else
            return null;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Remove AllVns embedded actions from a production by factoring them @out into
     * individual actionPart production using new non terminals. if the original
     * production was:
     * 
     * <pre>
     *    A ::= B {action1} C {action2} D
     * </pre>
     * 
     * then it will be factored into:
     * 
     * <pre>
     *    A ::= B NT$1 C NT$2 D
     *    NT$1 ::= {action1}
     *    NT$2 ::= {action2}
     * </pre>
     * 
     * where NT$1 and NT$2 are new system created non terminals.
     */

    /*
     * the declarations added to the parent production are also passed along, as
     * they should be perfectly valid in this code string, since it was originally a
     * code string in the parent, not on its own. frank 6/20/96
     */
    public void removeEmbeddedActions() /*throws internal_error*/ {
        Vn new_nt;
        String declare_str;
        int lastLocation = -1;
        /* walk over the production and process each actionPart */
        for (int act_loc = 0; act_loc < rhsLength; act_loc++)
            if (rhs(act_loc).IsAction) {

                declare_str = declareLabels(_rhs, act_loc, "");
                /* create a new non terminal for the actionPart production */
                new_nt = Vn.createNew(null, lhs.TheSymbolDraft.StackType); // TUM 20060608 embedded actions patch
                new_nt.is_embedded_action = true; /* 24-Mar-1998, CSA */

                /* create a new production with just the actionPart */
                new ActionProduction(this, new_nt, null, 0,
                    declare_str + ((ActionPart)rhs(act_loc)).CodeString,
                    lastLocation == -1 ? -1 : act_loc - lastLocation);

                /* replace the actionPart with the generated non terminal */
                _rhs[act_loc] = new SymbolPart(new_nt);
                lastLocation = act_loc;
            }
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Check to see if the production (now) appears to be nullable. A production is
     * nullable if its RHS could derive the empty string. This results when the RHS
     * is empty or contains only non terminals which themselves are nullable.
     */
    public bool checkNullable() /*throws internal_error*/ {
        ProductionPart part;
        SymbolDraft symbol;
        int pos;

        /* if we already know bail @out early */
        if (nullableKnown)
            return nullable;

        /* if we have a zero Count RHS we are directly nullable */
        if (rhsLength == 0) {
            /* stash and return the result */
            return setNullable(true);
        }

        /* otherwise we need to test AllVns of our parts */
        for (pos = 0; pos < rhsLength; pos++) {
            part = rhs(pos);

            /* only look at non-actions */
            if (!part.IsAction) {
                symbol = ((SymbolPart)part).TheSymbolDraft;

                /* if its a terminal we are definitely not nullable */
                if (!symbol.IsVn)
                    return setNullable(false);
                /* its a non-term, is it marked nullable */
                else if (!((Vn)symbol).nullable)
                    /* this one not (yet) nullable, so we aren't */
                    return false;
            }
        }

        /* if we make it here AllVns parts are nullable */
        return setNullable(true);
    }

    /** set (and return) nullable */
    public bool setNullable(bool v) {
        _nullable_known = true;
        _nullable = v;
        return v;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /**
     * Update (and return) the first set based on current NT firsts. This assumes
     * that nullable has already been computed for AllVns non terminals and
     * AllProdctions.
     */
    public VtSet checkFirstSet() /*throws internal_error*/ {
        int part;
        SymbolDraft symbol;

        /* walk down the _right hand side till we get past AllVns nullables */
        for (part = 0; part < rhsLength; part++) {
            /* only look at non-actions */
            if (!rhs(part).IsAction) {
                symbol = ((SymbolPart)rhs(part)).TheSymbolDraft;

                /* is it a non-terminal? */
                if (symbol.IsVn) {
                    /* TryAdd in current firsts from that NT */
                    _first_set.AddRange(((Vn)symbol).firstSet);

                    /* if its not nullable, we are done */
                    if (!((Vn)symbol).nullable)
                        break;
                }
                else {
                    /* its a terminal -- TryAdd that to the set */
                    _first_set.Add((Vt)symbol);

                    /* we are done */
                    break;
                }
            }
        }

        /* return our updated first set */
        return FirstSet;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Equality comparison. */
    public bool equals(ProductionDraft other) {
        if (other == null)
            return false;
        return other._index == _index;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Generic equality comparison. */
    /*@Override*/
    public override bool Equals(Object? other) {
        if (!(other is ProductionDraft))
            return false;
        else
            return equals((ProductionDraft)other);
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Produce a hash code. */
    /*@Override*/
    public override int GetHashCode() {
        /* just use a simple function of the index */
        return _index * 13;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Convert to a string. */
    /*@Override*/
    public override string ToString() {
        String result;

        /* catch any internal errors */
        try {
            result = "production [" + Index + "]: ";
            result += lhs != null ? lhs.ToString() : "$$NULL-LHS$$";
            result += " :: = ";
            for (int i = 0; i < rhsLength; i++)
                result += rhs(i) + " ";
            result += ";";
            if (actionPart != null && actionPart.CodeString != null)
                result += " {" + actionPart.CodeString + "}";

            if (nullableKnown)
                if (nullable)
                    result += "[NULLABLE]";
                else
                    result += "[NOT NULLABLE]";
        }
        catch (InternalError e) {
            /*
             * crash on internal error since we can't throw it from here (because superclass
             * does not throw anything.
             */
            e.crash();
            result = null;
        }

        return result;
    }

    /* . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . */

    /** Convert to a simpler string. */
    public String toSimpleString() /*throws internal_error*/ {
        String result;

        result = lhs != null ? lhs.TheSymbolDraft.Name : "NULL_LHS";
        result += " ::= ";
        for (int i = 0; i < rhsLength; i++)
            if (!rhs(i).IsAction)
                result += ((SymbolPart)rhs(i)).TheSymbolDraft.Name + " ";

        return result;
    }

    /*-----------------------------------------------------------*/

}
